﻿#region License

// Another free, open solution by folks who get it.
// Copyright © 2011 Business in Unison, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System.IO;
using System.Reflection;

using FluentValidation;
using FluentValidation.Results;

using Patterns.Text;

namespace Patterns.Inversion
{
	/// <summary>
	/// 	Validates the <see cref = "ServiceProviderContext" /> type by guaranteeing
	/// 	that all required fields have been set, that the indicated assembly is
	/// 	accessible, and that the indicated type is valid. Causes the type's
	/// 	assembly to be loaded into memory.
	/// </summary>
	public class ServiceProviderValidator : AbstractValidator<ServiceProviderContext>
	{
		private const string _assemblyNameKey = "assemblyName";
		private static readonly CompiledRegex _assemblyNamePattern = string.Format(@"^[^,]+,\s+(?<{0}>.+)", _assemblyNameKey);

		/// <summary>
		/// Initializes a new instance of the <see cref="ServiceProviderValidator"/> class.
		/// </summary>
		public ServiceProviderValidator()
		{
			RuleFor(context => context.ContainerName).NotEmpty();
			RuleFor(context => context.TypeName).NotEmpty();
			Custom(context =>
			{
				if(!_assemblyNamePattern.IsMatch(context.TypeName)) return new ValidationFailure("TypeName", "Should be recognizable as a type name");
				return !TypeExists(context) ? new ValidationFailure("TypeName", "Expected a valid, accessible type") : null;
			});
		}

		private static bool TypeExists(ServiceProviderContext context)
		{
			string assemblyName = _assemblyNamePattern.Match(context.TypeName).Groups[_assemblyNameKey].Value;
			string assemblyPath = Path.GetFullPath(assemblyName + ".dll");
			if (!File.Exists(assemblyPath)) return false;
			Assembly assembly = Assembly.LoadFrom(assemblyPath);
			context.ValidatedRegistryType = assembly.GetType(context.TypeName, false, true);
			return context.ValidatedRegistryType != null;
		}
	}
}